#include "ffmpegwrapper.h"
#include <QProcess>
#include <QTime>
#include <QDebug>
#include <QFile>

FFmpegWrapper::FFmpegWrapper(QObject *parent) :
    QObject(parent)
{
    ffmpeg = new QProcess(this);
    connect(ffmpeg, SIGNAL(readyReadStandardError()), this, SLOT(readFFmpegStandardError()));
    connect(ffmpeg, SIGNAL(finished(int)), this, SIGNAL(finished(int)));
}

FFmpegWrapper::~FFmpegWrapper()
{
    if (ffmpeg->state() != QProcess::NotRunning)
    {
        ffmpeg->blockSignals(true);
        ffmpeg->terminate();
        ffmpeg->waitForFinished();
        QFile::remove(outputFile);
    }
}

void FFmpegWrapper::init(const QString &_inputFile)
{
    inputFile = _inputFile;

    QProcess ffmpeg;
    ffmpeg.start("ffmpeg", QStringList() << "-i" << inputFile);
    ffmpeg.waitForFinished();
    QString output = ffmpeg.readAllStandardError();

    // find the soure resolution
    QRegExp rx("([1-9][0-9]*)x([1-9][0-9]*)");
    if (-1 == rx.indexIn(output))
        finished(-1);

    frameSize.setWidth(rx.cap(1).toInt());
    frameSize.setHeight(rx.cap(2).toInt());

    // find the duration
    rx.setPattern("Duration: ([0-9]*):([0-9]*):([0-9]*\\.[0-9]*)");
    if (-1 == rx.indexIn(output))
        finished(-1);

    int hours = rx.cap(1).toInt();
    int minutes = rx.cap(2).toInt();
    double seconds = rx.cap(3).toDouble();

    duration = 3600*hours + 60*minutes + seconds;
}

QSize FFmpegWrapper::fitTo(QSize size)
{
    int width = frameSize.width();
    int height = frameSize.height();
    int fitToWidth = size.width();
    int fitToHeight = size.height();
    if (fitToWidth*height/width/2*2 <= fitToHeight)
    {
        height = fitToWidth*height/width/2*2;
        width = fitToWidth;
    }
    else
    {
        width = fitToHeight*width/height/2*2;
        height = fitToHeight;
    }
    return QSize(width, height);
}

bool FFmpegWrapper::convert(const QString &_outputFile, bool forceH264, bool cut, const QTime &from, const QTime &to)
{
    outputFile = _outputFile;
    QSize outputFrameSize = forceH264 ? fitTo(QSize(320, 240)) : fitTo(QSize(640, 360));
    int outputWidth = outputFrameSize.width();
    int outputHeight = outputFrameSize.height();

    // prepare the command line arguments passed to ffmpeg
    QStringList args;
    args
          << "-i" << inputFile << "-sameq" << "-f" << "mp4" << "-threads" << "4" << "-r" << "30"
          << "-s" << QString("%1x%2").arg(outputWidth).arg(outputHeight)
          << "-acodec" << "libfaac" << "-ar" << "22050" << "-ab" << "64k" << "-ac" << "2" << "-async" << "1";

    // for 320x240 and below use H.264
    if (outputWidth <= 320 && outputHeight <= 240)
    {
        args
              << "-vcodec" << "libx264"
              << "-qcomp" << "0.6" << "-qmin" << "11" << "-qmax" << "51" << "-qdiff" << "4"
              << "-flags" << "+loop" << "-cmp" << "+chroma" << "-subq" << "7" << "-refs" << "6"
              << "-g" << "250" << "-keyint_min" << "25" << "-rc_eq" << "blurCplx^(1-qComp)"
              << "-sc_threshold" << "40" << "-me_range" << "12" << "-i_qfactor" << "0.71"
              << "-directpred" << "3"
              << "-crf" << "26";
    }
    // for 640x360 use MPEG-4
    else
    {
        args << "-vcodec" << "mpeg4";
    }

    if (cut)
    {
        int fromSeconds = 60 * from.minute() + from.second();
        int toSeconds = 60 * to.minute() + to.second();
        args << "-ss" << QString("%1").arg(fromSeconds) << "-t" << QString("%1").arg(toSeconds - fromSeconds);
    }

    args << "-y" << outputFile;

    qDebug() << args.join(" ");

    ffmpeg->start("ffmpeg", args);
    ffmpeg->waitForStarted();
    if (ffmpeg->state() == QProcess::NotRunning)
        return false;

    return true;
}

void FFmpegWrapper::readFFmpegStandardError()
{
    output += ffmpeg->readAllStandardError();

    QRegExp rx("time=([0-9]*\\.[0-9]*)");
    int pos = rx.lastIndexIn(output);
    if (-1 != pos)
    {
        output.remove(0, pos + rx.matchedLength());

        double seconds = rx.cap(1).toDouble();
        progressNotification(10 * seconds);
    }
}
